package virtualdatagramsocket;

import java.net.*;
import java.io.*;
import java.util.Random;

public class VirtualDatagramSocket extends DatagramSocket {

	/**
	 * The loss rate of packets.
	 * 0   = none
	 * 100 = all
	 */
	private int packetLossRate;
	/**
	 * The rate of highly delayed packets
	 * 0   = all packets are sent immediately
	 * 100 = all packets are highly delayed 
	 */
	private int packetDelayRate;
	/**
	 * If a packet is chosen to be delayed,
	 * the time waited before sending it in milliseconds
	 */
	private int packetDelay;
	
	/**
	 * Gets the packet loss rate in the socket
	 * @return The packet loss rate
	 */
	public int getPacketLossRate() {
		return this.packetLossRate;
	}
	
	/**
	 * Sets the loss rate of packets in the socket
	 * @param packetLossRate The packet loss rate. 0 = none, 100 = all
	 * @throws Exception If the packet loss was not between 0..100
	 */
	public void setPacketLossRate(int packetLossRate) throws Exception {
		if (packetLossRate >= 0 && packetLossRate <= 100) {
    		this.packetLossRate = packetLossRate;
		} else {
			throw new Exception("Packet loss rate must be between 0..100");
		}
	}
	
	/**
	 * @return The rate of highly delayed packets
	 */
	public int getPacketDelayRate() {
		return this.packetDelayRate;
	}
	
	/**
	 * Sets the rate of highly delayed packets
	 * @param packetDelayRate The rate of highly delayed packets
	 * @throws Exception If the given rate is not between 0..100
	 */
	public void setPacketDelayRate(int packetDelayRate) throws Exception {
		if (packetDelayRate >= 0 && packetDelayRate <= 100) {
			this.packetDelayRate = packetDelayRate;
		} else {
			throw new Exception("Packet delay rate must be between 0..100");
		}
	}
	
	/**
	 * @return The time waited before a packet is sent
	 * if it is chosen to be delayed
	 */
	public int getPacketDelay() {
		return this.packetDelay;
	}
	
	/**
	 * Sets time waited before a packet is sent if it is
	 * chosen to be delayed
	 * @param packetDelay The waited time in milliseconds
	 */
	public void setPacketDelay(int packetDelay) {
    	this.packetDelay = packetDelay;
	}
	
	/**
	 * Constructor
	 * @throws SocketException If there's errors. See parent class
	 * for more information
	 */
	public VirtualDatagramSocket() throws SocketException {
		super();
		this.initialize();
	}
	
	/**
	 * Constructor 
	 * @param port The port the socket is bound to
	 * @throws SocketException If there's errors. See parent class
	 * for more information
	 */
	public VirtualDatagramSocket(int port) throws SocketException {
		super(port);
		this.initialize();
	}
	
	/**
	 * Constructor
	 * @param port The port the socket is bound to
	 * @param laddr The local address the socket is bound to
	 * @throws SocketException If there's errors. See parent class
	 * for more information
	 */
	public VirtualDatagramSocket(int port, InetAddress laddr) throws SocketException {
		super(port, laddr);
		this.initialize();
	}

	/**
	 * Initializes the loss and delay rates
	 */
	private void initialize() {
		try {
			this.setPacketLossRate(20);
			this.setPacketDelayRate(20);
			this.setPacketDelay(5000);
		} catch (Exception e) {
			/**
			 * Never happens if the default 
			 * rates are set correctly
			 */
			System.err.println(e.toString());
		}
	}
	
	/**
	 * @override The override version loses packets according
	 * to the set packet loss rate
	 */
	public void send(DatagramPacket p) throws IOException {
		Random generator = new Random();
		/**
		 * Used to determinate if a packet is lost
		 */
		int n = generator.nextInt(100);
		/**
		 * Used to determinate if a packet is delayed
		 */
		int m = generator.nextInt(100);
		/**
		 * For values of n between 0..lossrate the packet is lost and
		 * for values of n between lossrate..100 the packet is sent
		 */
		if (n <= this.getPacketLossRate()) {
			/**
			 * The packet was lost.
			 */
		} else {
			if (m <= this.getPacketDelayRate()) {
				/**
				 * The packet was delayed. Wait the defined time
				 */
				try {
					Thread.sleep(this.getPacketDelay());
				} catch (InterruptedException e) {
					System.err.println(e.toString());
				}
			}
			super.send(p);
		}
	}
		
}